package sk.tuke.constraintbuilder.views;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.wb.swt.SWTResourceManager;

import sk.tuke.constraintbuilder.editor.TemplateEditorController;
import sk.tuke.constraintbuilder.model.RuleElement;
import sk.tuke.constraintbuilder.model.Model;
import sk.tuke.constraintbuilder.model.SimpleRuleElement;
import sk.tuke.constraintbuilder.model.StructuredRuleElement;
import sk.tuke.constraintbuilder.model.Template;
import sk.tuke.constraintbuilder.utils.MyImages;
import sk.tuke.constraintbuilder.views.components.ConstraintEditorDropAdapter;
import sk.tuke.constraintbuilder.views.components.IRulePanel;
import sk.tuke.constraintbuilder.views.components.RuleChooserPanel;
import sk.tuke.constraintbuilder.views.components.SimpleElementRulePanel;
import sk.tuke.constraintbuilder.views.components.StructuredElementRulePanel;


public class TemplateEditorView extends Composite {

	private final FormToolkit toolkit = new FormToolkit(Display.getCurrent());
	
	//Controller
	private TemplateEditorController controller;
	
	//Gui Components
	private java.util.List<IRulePanel> rulePanels = new ArrayList<>();
	private Text nameTextfield;
	private Text descriptionText;
	private Form editorForm;
	private Section rulesSection;
	private Composite rulesSectionPanel;
	private Section conditionSection;
	private Composite conditionSectionPanel;
	private Label emptyRulePanelLabel;
	private Label emptyCondPanelLabel;

	/**
	 * Create GUI for template editor
	 * @param parent a widget which will be the parent of the new instance
	 * @param controller2 a controller which control communication between model (template) and view (editor gui)
	 */
	public TemplateEditorView(Composite parent, TemplateEditorController controller2) {
		super(parent, SWT.NONE);
		this.controller=controller2;
		setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
		addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				toolkit.dispose();
			}
		});
		toolkit.adapt(this);
		toolkit.paintBordersFor(this);
		setLayout(new FillLayout(SWT.HORIZONTAL));
		
		editorForm = toolkit.createForm(this);
		toolkit.paintBordersFor(editorForm);
		editorForm.setText("Template Editor");
		editorForm.getBody().setLayout(new FillLayout(SWT.HORIZONTAL));
		
		//LEFT PANEL//////////////////////////////////////////////////////////
		Composite leftPanel = toolkit.createComposite(editorForm.getBody(), SWT.NONE);
		leftPanel.setLayout(new GridLayout(1, false));
		toolkit.paintBordersFor(leftPanel);
		
		//RULES SECTION
		DropTargetAdapter rulePanelDropListener = new ConstraintEditorDropAdapter() {
			@Override
			public void drop(DropTargetEvent event) {
				if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
			        DropTarget target = (DropTarget) event.widget;
			        String data = (String) event.data;
			        StringTokenizer st = new StringTokenizer(data, ":");
			        String rule = st.nextToken();
			        String ruleType = st.nextToken();
			        if(target.getControl() instanceof Composite){
			        	String panelName = (String) target.getControl().getData("name");
			        	if(panelName!=null){
			        		if (panelName.equals("rulesSectionPanel")) {
			        			controller.addRuleElementToRules(ruleType, rule);
							}
			        		else if (panelName.equals("conditionSectionPanel")) {
			        			controller.addRuleElementToConditions(ruleType, rule);
							}
			        	}
			        }
			    }
			}
		};
		
		rulesSection = toolkit.createSection(leftPanel, Section.TWISTIE | Section.TITLE_BAR);
		GridData gd_rulesSection = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
		gd_rulesSection.heightHint = 57;
		rulesSection.setLayoutData(gd_rulesSection);
		toolkit.paintBordersFor(rulesSection);
		rulesSection.setText("Rules");
		rulesSection.setExpanded(true);
		
		rulesSectionPanel = toolkit.createComposite(rulesSection, SWT.NONE);
		toolkit.paintBordersFor(rulesSectionPanel);
		rulesSection.setClient(rulesSectionPanel);
		rulesSectionPanel.setLayout(new GridLayout(1, false));
		rulesSectionPanel.setData("name", "rulesSectionPanel");
		DropTarget target = new DropTarget(rulesSectionPanel, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
		target.setTransfer(new Transfer[] {TextTransfer.getInstance()});
		target.addDropListener(rulePanelDropListener);
		
		emptyRulePanelLabel = new Label(rulesSectionPanel, SWT.NONE);
		toolkit.adapt(emptyRulePanelLabel, true, true);
		emptyRulePanelLabel.setText("[Drag here some rules]");

		//CONDITON SECTION
		conditionSection = toolkit.createSection(leftPanel, Section.TWISTIE | Section.TITLE_BAR);
		conditionSection.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		toolkit.paintBordersFor(conditionSection);
		conditionSection.setText("Condition");
		conditionSection.setExpanded(true);
				
		conditionSectionPanel = toolkit.createComposite(conditionSection, SWT.NONE);
		toolkit.paintBordersFor(conditionSectionPanel);
		conditionSection.setClient(conditionSectionPanel);
		conditionSectionPanel.setLayout(new GridLayout(1, false));
		conditionSectionPanel.setData("name", "conditionSectionPanel");
		target = new DropTarget(conditionSectionPanel, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
		target.setTransfer(new Transfer[] {TextTransfer.getInstance()});
		target.addDropListener(rulePanelDropListener);
		
		emptyCondPanelLabel = new Label(conditionSectionPanel, SWT.NONE);
		toolkit.adapt(emptyCondPanelLabel, true, true);
		emptyCondPanelLabel.setText("[Drag here some rules]");

		//Save constraint
		Section saveSection = toolkit.createSection(leftPanel, Section.TWISTIE | Section.TITLE_BAR);
		saveSection.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		toolkit.paintBordersFor(saveSection);
		saveSection.setText("Save Template");
		saveSection.setExpanded(true);
		
		Composite saveSectionPanel = toolkit.createComposite(saveSection, SWT.NONE);
		toolkit.paintBordersFor(saveSectionPanel);
		saveSection.setClient(saveSectionPanel);
		saveSectionPanel.setLayout(new GridLayout(3, false));
		
		Label descriptionLabel = new Label(saveSectionPanel, SWT.NONE);
		toolkit.adapt(descriptionLabel, true, true);
		descriptionLabel.setText("Description: ");
		
		descriptionText = new Text(saveSectionPanel, SWT.BORDER);
		descriptionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		toolkit.adapt(descriptionText, true, true);
		
		Label nameLabel = new Label(saveSectionPanel, SWT.NONE);
		nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		toolkit.adapt(nameLabel, true, true);
		nameLabel.setText("Name: ");
		
		nameTextfield = new Text(saveSectionPanel, SWT.BORDER);
		nameTextfield.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		toolkit.adapt(nameTextfield, true, true);
		
		Button saveButton = new Button(saveSectionPanel, SWT.NONE);
		saveButton.setToolTipText("Save Changes");
		toolkit.adapt(saveButton, true, true);
		saveButton.setText("Save");
		saveButton.addSelectionListener(new SelectionAdapter() {
	    	public void widgetSelected(SelectionEvent e) {
	    		doSave();
	    	}
		});	
		
		//RIGHT PANEL//////////////////////////////////////////////////////////
		Composite rightPanel = toolkit.createComposite(editorForm.getBody(), SWT.NONE);
		toolkit.paintBordersFor(rightPanel);
		rightPanel.setLayout(new GridLayout(1, false));
		
		//Rules section
		Section ruleChooserSection = toolkit.createSection(rightPanel, Section.TWISTIE | Section.TITLE_BAR);
		ruleChooserSection.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
		toolkit.paintBordersFor(ruleChooserSection);
		ruleChooserSection.setText("Available Rules");
		ruleChooserSection.setExpanded(true);
		Composite ruleChooserComposite = new RuleChooserPanel(ruleChooserSection, SWT.NONE);
		ruleChooserSection.setClient(ruleChooserComposite);		
	}
	
	/**
	 * Show template specified as parameter in GUI
	 * @param template to show
	 */
	public void showTemplate(Template template){
		editorForm.setText("Template: "+template.getName());
		nameTextfield.setText(template.getName());
		if(template.getDescription()!=null){
			descriptionText.setText(template.getDescription());
		}
		//Show rule's rulepanels in rulesSection
		java.util.List<RuleElement> ruleElements = template.getRule().getElements();
		for (RuleElement element : ruleElements) {
			addRulePanel(element);
		}
		//If conditions exists, show its rulepanels in conditionSection 
		try{
			java.util.List<RuleElement> condElements = template.getCondition().getElements();
			for (RuleElement element : condElements) {
				addCondPanel(element);
			}
		}catch (NullPointerException e) {
			// Condition not exists - we can ignore it
		}
	}
	
	/**
	 * Add new rulepanel for specified element(rule) to ruleSection 
	 * @param element (from model) to show
	 */
	public void addRulePanel(RuleElement element){
		if(!emptyRulePanelLabel.isDisposed())emptyRulePanelLabel.dispose();
		if(element instanceof SimpleRuleElement) rulePanels.add(new SimpleElementRulePanel(rulesSectionPanel, (SimpleRuleElement) element, controller, true));
		else if(element instanceof StructuredRuleElement) rulePanels.add(new StructuredElementRulePanel(rulesSectionPanel, (StructuredRuleElement) element, controller, true));
	}
	
	/**
	 * Add new rulepanel for specified element(condition) to conditionSection
	 * @param element (from model) to show
	 */
	public void addCondPanel(RuleElement element){
		if(!emptyCondPanelLabel.isDisposed())emptyCondPanelLabel.dispose();
		if(element instanceof SimpleRuleElement) rulePanels.add(new SimpleElementRulePanel(conditionSectionPanel, (SimpleRuleElement) element, controller, true));
		else if(element instanceof StructuredRuleElement) rulePanels.add(new StructuredElementRulePanel(conditionSectionPanel, (StructuredRuleElement) element, controller, true));
	}

	/**
	 * Save created or edited template
	 */
	public void doSave(){
		for (IRulePanel panel : rulePanels) {
			panel.doSave();
		}
		controller.saveTemplate(nameTextfield.getText(), descriptionText.getText());
	}
}
